Popular Searches
Popular Course Categories
Popular Courses

Moving between application screens

Moving between application screens

Flutter Navigation & Screens

Moving Between Application Screens in Flutter

Moving between application screens is a fundamental part of Flutter development. Real-world applications usually contain multiple screens such as Login, Home, Profile, Products, Product Details, Cart, Settings, and Checkout.

Flutter provides navigation and routing features that allow users to move between these screens. In Flutter, screens and pages are commonly represented as routes. The Navigator manages these routes as a stack, allowing an application to move forward to a new screen and backward to a previous screen.


1. What Does Moving Between Screens Mean?

Moving between screens means changing the currently visible screen when a user performs an action such as tapping a button, selecting a list item, logging in, or opening a product.

For example, a shopping application may have the following flow:

Home Screen
     |
     v
Product List
     |
     v
Product Details
     |
     v
Cart
     |
     v
Checkout

Flutter navigation allows the user to move forward through this flow and return to previously visited screens.


2. What is a Route?

In Flutter, a screen or page is commonly represented as a route. A route is usually created from a widget.

For example:

HomeScreen
ProfileScreen
SettingsScreen
ProductScreen
ProductDetailsScreen

These widgets can be displayed as routes during application navigation.


3. What is Navigator?

The Navigator manages a stack of routes. When a user moves to another screen, a new route is generally added to the stack. When the user goes back, the current route is removed from the stack.

Navigation Stack Example

Initial:
[Home]

Open Products:
[Home, Products]

Open Details:
[Home, Products, Details]

Open Cart:
[Home, Products, Details, Cart]

Press Back:
[Home, Products, Details]

Press Back:
[Home, Products]

This stack-based approach allows Flutter to maintain navigation history.


4. Basic Navigation Flow

The basic screen navigation flow consists of three important steps:

  1. Create the screens that need to be displayed.
  2. Use Navigator.push() to move to another screen.
  3. Use Navigator.pop() to return to the previous screen.
Screen A
   |
   | Navigator.push()
   v
Screen B
   |
   | Navigator.pop()
   v
Screen A

5. Using Navigator.push()

Navigator.push() adds a new route to the navigation stack and displays it.

Syntax

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const SecondScreen(),
  ),
);

Important Parts

PartDescription
Navigator.push()Adds a new route to the navigation stack.
contextProvides the current widget's location in the widget tree.
MaterialPageRouteCreates a Material-style route.
builderBuilds the destination screen.

6. Simple Example of Moving to Another Screen

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Screen Navigation',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
        ),
        useMaterial3: true,
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const DetailsScreen(),
              ),
            );
          },
          child: const Text('Open Details'),
        ),
      ),
    );
  }
}

class DetailsScreen extends StatelessWidget {
  const DetailsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Details Screen'),
      ),
      body: const Center(
        child: Text(
          'Welcome to Details Screen',
          style: TextStyle(fontSize: 22),
        ),
      ),
    );
  }
}

How the Code Works

  1. The application starts with HomeScreen.
  2. The user presses the Open Details button.
  3. Navigator.push() is called.
  4. A MaterialPageRoute is created.
  5. DetailsScreen is displayed.
  6. The new route is placed above the Home route in the navigation stack.

7. Returning to the Previous Screen with Navigator.pop()

Navigator.pop() removes the current route from the navigation stack and displays the previous route.

Syntax

Navigator.pop(context);

Example

ElevatedButton(
  onPressed: () {
    Navigator.pop(context);
  },
  child: const Text('Go Back'),
)

If the stack contains:

[HomeScreen, DetailsScreen]

after calling Navigator.pop(context) it becomes:

[HomeScreen]

8. Complete Push and Pop Example

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomeScreen(),
    ),
  );
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const ProfileScreen(),
              ),
            );
          },
          child: const Text('Open Profile'),
        ),
      ),
    );
  }
}

class ProfileScreen extends StatelessWidget {
  const ProfileScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Profile'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Back to Home'),
        ),
      ),
    );
  }
}

9. Navigator.of(context)

Navigation can also be performed using Navigator.of(context).

Push

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => const DetailsScreen(),
  ),
);

Pop

Navigator.of(context).pop();

Both Navigator.push() and Navigator.of(context).push() can be used for imperative navigation.


10. Moving Between Multiple Application Screens

Applications commonly have more than two screens. Each new navigation action can add another route to the stack.

Home
 |
 | push
 v
Products
 |
 | push
 v
Product Details
 |
 | push
 v
Cart
 |
 | push
 v
Checkout

The user can move backward through the stack:

Checkout
   |
   | pop
   v
Cart
   |
   | pop
   v
Product Details
   |
   | pop
   v
Products
   |
   | pop
   v
Home

11. Practical Three-Screen Example

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const ProductScreen(),
              ),
            );
          },
          child: const Text('Open Products'),
        ),
      ),
    );
  }
}

class ProductScreen extends StatelessWidget {
  const ProductScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Products'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const ProductDetailsScreen(),
              ),
            );
          },
          child: const Text('View Product'),
        ),
      ),
    );
  }
}

class ProductDetailsScreen extends StatelessWidget {
  const ProductDetailsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Product Details'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Go Back'),
        ),
      ),
    );
  }
}

12. Navigation Using ListTile

List items are frequently used to open other application screens.

ListTile(
  leading: const Icon(Icons.person),
  title: const Text('Profile'),
  trailing: const Icon(Icons.arrow_forward_ios),
  onTap: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => const ProfileScreen(),
      ),
    );
  },
)

Multiple Navigation Options

ListView(
  children: [
    ListTile(
      leading: const Icon(Icons.person),
      title: const Text('Profile'),
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => const ProfileScreen(),
          ),
        );
      },
    ),
    ListTile(
      leading: const Icon(Icons.settings),
      title: const Text('Settings'),
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => const SettingsScreen(),
          ),
        );
      },
    ),
  ],
)

13. Navigation Using Buttons

Buttons are another common way of moving between screens.

ElevatedButton

ElevatedButton(
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => const ProfileScreen(),
      ),
    );
  },
  child: const Text('Open Profile'),
)

TextButton

TextButton(
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => const SettingsScreen(),
      ),
    );
  },
  child: const Text('Open Settings'),
)

IconButton

IconButton(
  icon: const Icon(Icons.person),
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => const ProfileScreen(),
      ),
    );
  },
)

14. Navigation from an AppBar

The AppBar can contain navigation actions such as a back button or an icon that opens another screen.

Custom Back Button

AppBar(
  title: const Text('Profile'),
  leading: IconButton(
    icon: const Icon(Icons.arrow_back),
    onPressed: () {
      Navigator.pop(context);
    },
  ),
)

For standard Material routes, Flutter can automatically display a back button when there is a previous route in the navigation stack.


15. Passing Data While Moving Between Screens

Applications often need to send information from one screen to another. For example, when a user selects a product, the Product Details screen needs to receive information about that product.

Product Model

class Product {
  final String name;
  final double price;

  const Product({
    required this.name,
    required this.price,
  });
}

Product Details Screen

class ProductDetailsScreen extends StatelessWidget {
  final Product product;

  const ProductDetailsScreen({
    super.key,
    required this.product,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(product.name),
      ),
      body: Center(
        child: Text(
          'Price: ₹${product.price}',
          style: const TextStyle(fontSize: 22),
        ),
      ),
    );
  }
}

Passing Product Information

final product = Product(
  name: 'Flutter Course',
  price: 4999,
);

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) =>
        ProductDetailsScreen(product: product),
  ),
);

This approach passes the selected product directly to the destination screen through its constructor.


16. Returning Data from Another Screen

A destination screen can return a result to the screen that opened it. This is useful for selections, forms, filters, and other user interactions.

Return Data

Navigator.pop(context, 'Flutter');

Receive Data

final result = await Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const SelectionScreen(),
  ),
);

if (result != null) {
  print(result);
}

Complete Example

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  Future openSelection(BuildContext context) async {
    final result = await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => const SelectionScreen(),
      ),
    );

    if (result != null) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Selected: $result'),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => openSelection(context),
          child: const Text('Choose Option'),
        ),
      ),
    );
  }
}

class SelectionScreen extends StatelessWidget {
  const SelectionScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Selection'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context, 'Flutter');
          },
          child: const Text('Select Flutter'),
        ),
      ),
    );
  }
}

17. Navigator.pushReplacement()

Navigator.pushReplacement() replaces the current route with a new route.

Syntax

Navigator.pushReplacement(
  context,
  MaterialPageRoute(
    builder: (context) => const HomeScreen(),
  ),
);

Login Example

void login(BuildContext context) {
  Navigator.pushReplacement(
    context,
    MaterialPageRoute(
      builder: (context) => const HomeScreen(),
    ),
  );
}

A common navigation flow is:

Login Screen
     |
     | Login Successful
     v
Home Screen

This approach is useful when the current screen should be replaced instead of remaining as the previous route.


18. Navigator.pushAndRemoveUntil()

pushAndRemoveUntil() adds a new route and removes previous routes until a specified condition is satisfied.

Example

Navigator.pushAndRemoveUntil(
  context,
  MaterialPageRoute(
    builder: (context) => const HomeScreen(),
  ),
  (route) => false,
);

Using (route) => false removes the previous routes from the navigation stack.

Example Use Case

Login
  |
  v
Home
  |
  v
Products

After successful authentication, an application may navigate to Home and remove the Login route from the navigation history.


19. Navigator.popUntil()

Navigator.popUntil() removes routes from the stack until a specified condition becomes true.

Example

Navigator.popUntil(
  context,
  (route) => route.isFirst,
);

This returns the user to the first route in the navigation stack.

Example

[Home, Products, Details, Cart]

After:

Navigator.popUntil(
  context,
  (route) => route.isFirst,
);

The stack becomes:

[Home]

20. MaterialPageRoute

MaterialPageRoute creates a route suitable for Material applications and provides a Material-style transition.

Example

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const DetailsScreen(),
  ),
);

The destination widget is returned from the builder.


21. CupertinoPageRoute

For Cupertino-style applications, Flutter provides CupertinoPageRoute.

Example

import 'package:flutter/cupertino.dart';

Navigator.push(
  context,
  CupertinoPageRoute(
    builder: (context) => const DetailsScreen(),
  ),
);

This route provides a Cupertino-style transition.


22. Named Routes

Flutter supports named routes, where routes are identified using names such as /, /profile, and /settings.

Configuration

MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => const HomeScreen(),
    '/profile': (context) => const ProfileScreen(),
    '/settings': (context) => const SettingsScreen(),
  },
)

Navigate Using a Named Route

Navigator.pushNamed(
  context,
  '/profile',
);

Return

Navigator.pop(context);

Named routes are still supported, but current Flutter documentation does not recommend them for most applications. For straightforward navigation, Navigator with MaterialPageRoute can be used. Applications with more advanced routing and deep-linking requirements can use a routing package such as go_router.


23. Navigation with go_router

For applications that require structured routing, deep linking, nested navigation, or web URL synchronization, a routing package such as go_router can be considered.

Install go_router

flutter pub add go_router

Basic Router Configuration

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/profile',
      builder: (context, state) => const ProfileScreen(),
    ),
  ],
);

void main() {
  runApp(
    MaterialApp.router(
      routerConfig: router,
    ),
  );
}

Navigate to Profile

ElevatedButton(
  onPressed: () {
    context.go('/profile');
  },
  child: const Text('Open Profile'),
)

Go Back

context.pop();

24. Deep Linking

Deep linking allows a URL or external link to open a specific location inside an application.

Example URL

https://example.com/products/25

A routing configuration can use this path to open a specific Product Details screen.

Flutter supports navigation and deep linking across supported platforms. Applications with advanced deep-linking requirements can use Router-based navigation or a routing package such as go_router.


25. Navigation Methods Comparison

MethodPurpose
Navigator.push()Opens a new screen by adding a route to the stack.
Navigator.pop()Returns to the previous screen by removing the current route.
Navigator.pushReplacement()Replaces the current route with a new route.
Navigator.pushAndRemoveUntil()Adds a route and removes previous routes according to a condition.
Navigator.popUntil()Removes routes until a specified condition is met.
Navigator.pushNamed()Navigates to a configured named route.

26. Complete Example: Login to Home to Profile

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const LoginScreen(),
    );
  }
}

class LoginScreen extends StatelessWidget {
  const LoginScreen({super.key});

  void login(BuildContext context) {
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(
        builder: (context) => const HomeScreen(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Login'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => login(context),
          child: const Text('Login'),
        ),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const ProfileScreen(),
                ),
              );
            },
          ),
        ],
      ),
      body: const Center(
        child: Text(
          'Welcome Home',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

class ProfileScreen extends StatelessWidget {
  const ProfileScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Profile'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Back to Home'),
        ),
      ),
    );
  }
}

Flow

Login
  |
  | pushReplacement
  v
Home
  |
  | push
  v
Profile
  |
  | pop
  v
Home

27. Common Navigation Mistakes

Mistake 1: Passing a Widget Directly to Navigator.push()

Navigator.push(
  context,
  const DetailsScreen(),
);

This is incorrect because Navigator.push() expects a Route.

Correct Version

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const DetailsScreen(),
  ),
);

Mistake 2: Creating Unnecessary Navigation History

Repeatedly pushing the same screen can create an unnecessarily large navigation stack. Use the appropriate navigation method for the intended flow.

Mistake 3: Passing Data Incorrectly

Make sure the destination screen receives the correct type and required values when data is passed through a constructor.

Mistake 4: Using Named Routes for Every Application

Named routes are supported, but Flutter's current documentation recommends considering Navigator with MaterialPageRoute or a routing package such as go_router depending on the application's routing requirements.


28. Best Practices

  • Use Navigator.push() for straightforward screen-to-screen navigation.
  • Use Navigator.pop() to return to the previous screen.
  • Use pushReplacement() when the current route should be replaced.
  • Use pushAndRemoveUntil() when previous navigation history needs to be removed.
  • Pass screen-specific data explicitly through constructors when practical.
  • Use typed results when returning data from another screen.
  • Keep navigation flows predictable and easy to understand.
  • For complex routing requirements, consider a structured routing solution.
  • Test navigation flows on every platform supported by the application.

29. Practice Exercise

Create a Flutter application with the following screen structure:

Home
 |
 +----> Profile
 |
 +----> Products
 |        |
 |        +----> Product Details
 |
 +----> Settings

Requirements

  1. Create a Home Screen.
  2. Add a Profile navigation button.
  3. Add a Products navigation button.
  4. Create a Product List screen.
  5. Open Product Details when a product is selected.
  6. Pass product information to Product Details.
  7. Create a Settings screen.
  8. Use Navigator.pop() to return to previous screens.
  9. Use pushReplacement() in a Login-to-Home flow.
  10. Experiment with pushAndRemoveUntil().

30. Interview Questions

Q1. How do you move from one screen to another in Flutter?

Use Navigator.push() with a route such as MaterialPageRoute.

Q2. How do you return to the previous screen?

Use Navigator.pop(context).

Q3. What is a route?

A route represents a screen or page that can be displayed by the application.

Q4. What is Navigator?

Navigator manages a stack of routes and provides methods for moving between them.

Q5. What does Navigator.push() do?

It adds a new route to the navigation stack and displays it.

Q6. What does Navigator.pop() do?

It removes the current route and returns to the previous route.

Q7. How can data be passed between screens?

Data can be passed through constructor parameters or through route arguments in suitable routing configurations.

Q8. How can a screen return data?

Use Navigator.pop(context, result) and await the result from Navigator.push().

Q9. What is pushReplacement()?

It replaces the current route with a new route.

Q10. When can go_router be useful?

It can be useful for applications with structured routing, advanced navigation, deep linking, or web URL synchronization requirements.


31. Quick Revision Table

TaskFlutter Code/Approach
Open another screenNavigator.push()
Go backNavigator.pop()
Replace current screenNavigator.pushReplacement()
Remove previous routesNavigator.pushAndRemoveUntil()
Return to first routeNavigator.popUntil()
Pass dataConstructor parameters
Return dataNavigator.pop(context, result)
Material-style routeMaterialPageRoute
Cupertino-style routeCupertinoPageRoute
Advanced routingRouter or routing package such as go_router

32. Key Takeaways

  • Flutter applications commonly contain multiple screens.
  • Flutter screens and pages are represented as routes.
  • Navigator manages a stack of routes.
  • Navigator.push() moves to a new screen.
  • Navigator.pop() returns to the previous screen.
  • MaterialPageRoute can be used for Material-style navigation.
  • CupertinoPageRoute can be used for Cupertino-style navigation.
  • Data can be passed between screens using constructor parameters.
  • A screen can return data using Navigator.pop().
  • pushReplacement() replaces the current route.
  • pushAndRemoveUntil() can remove previous routes from the stack.
  • Advanced applications can use Router-based navigation or a routing package such as go_router.

33. Official Flutter Documentation


34. JustAcademy Flutter Training Resources

For additional Flutter learning resources and course information, visit the following links:

whatsapp